/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_REFLECTION_LIST_HPP
#define	_CUBEFS_CORE_API_REFLECTION_LIST_HPP

#include <cubefs/api/reflection/list.hpp>
#include <cubefs/api/exceptions/bounds.hpp>

#include <string>
#include <sstream>
#include <iostream>

#define CUBEFS_SOURCE "cubefs/core/api/reflection/list.hpp:" CUBEFS_AT

namespace cubefs    {
namespace api       {


template <typename Interface>
class ListImplT: public ValueImplT<Interface> {
	typedef ListImplT<Interface> 	Me;
	typedef ValueImplT<Interface>	Base;
public:

	virtual bool IsElementPrimitive() const	{
		return false;
	}

	virtual Typed* GetElement(Int index) const {
		throw CubefsException(CUBEFS_SOURCE, "Method is not implemented for this list");
	}

	virtual void GetElement(Int index, Value*) const {
		throw CubefsException(CUBEFS_SOURCE, "Method is not implemented for this list");
	}

	virtual void SetElement(Int index, const Typed* value) {
		throw CubefsException(CUBEFS_SOURCE, "Method is not implemented for this list");
	}

	virtual Value* CreateValueHolder() const {
		throw CubefsException(CUBEFS_SOURCE, "Method is not implemented for this list");
	}

	virtual void Insert(Int index, const Typed* value) {
		throw CubefsException(CUBEFS_SOURCE, "Method is not implemented for this list");
	}

	virtual void Add(const Typed* value) {
		throw CubefsException(CUBEFS_SOURCE, "Method is not implemented for this list");
	}

	static void InitType() {
		type_ = new AbstractType(TypeNameFactory<Interface>::name(), Value::MyType());
	}

    static void InitOperations() {
    	AbstractType* type = static_cast<AbstractType*>(Interface::MyType());

    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "size", &Me::size));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int)>(type, "Remove", "index", &Me::Remove));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, Int)>(type, "RemoveAll", "from", "to", &Me::RemoveAll));
    	type->AddOperation(new BasicOperation<void (Me::*)()>(type, "Clear", &Me::Clear));
    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "GetElementSize", &Me::GetElementSize));

    	type->AddOperation(new BasicOperation<Typed* (Me::*)(Int), Typed* (Me::*)(Int) const>(type, Typed::MyType(), "GetElement", "index", &Me::GetElement));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, Value*), void (Me::*)(Int, Value*) const>(type, "GetElement", "index", "value", &Me::GetElement));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, Typed*), void (Me::*)(Int, const Typed*)>(type, "SetElement", "index", "value", &Me::SetElement));

    	type->AddOperation(new BasicOperation<void (Me::*)(Int, Typed*), void (Me::*)(Int, const Typed*)>(type, "Insert", "index", "value", &Me::Insert));
    	type->AddOperation(new BasicOperation<void (Me::*)(Typed*), void (Me::*)(const Typed*)>(type, "Add", "value", &Me::Add));
    }

    CUBEFS_DECLARE_REFLECTION;
};

template <typename Interface>
Type* ListImplT<Interface>::type_ 					= NULL;

typedef ListImplT<List> ListImpl;

template <typename T, Int Size, typename Interface> class TypedListImplT;

template <typename T, typename Interface>
class TypedListImplT<T, -1, Interface>: public ListImplT<Interface> {
	typedef TypedListImplT<T, -1, Interface> Me;
	typedef ListImplT<Interface>			Base;

    std::vector<T> list_;

public:
    typedef T Element;

    TypedListImplT() {}
    TypedListImplT(Int capacity): list_(capacity) {}

    virtual ~TypedListImplT() throw () {}

    virtual Int GetElementSize() const {
        return sizeof(T);
    }

    virtual Int size() const {
        return list_.size();
    }

    std::vector<T>& list() {
        return list_;
    }

    const std::vector<T>& list() const {
        return list_;
    }

    virtual const T& Get(Int index) const
    {
        if (index >=0 && index < list_.size()) {
            return list_[index];
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, list_.size());
        }
    }

    virtual void Set(Int index, const T &value)
    {
    	if (index >=0 && index < list_.size()) {
    		list_[index] = value;
    	}
    	else {
    		throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, list_.size());
    	}
    }

    const T& operator[](Int index) const {
        return list_[index];
    }

    T& operator[](Int index) {
        return list_[index];
    }

    virtual void Clear() {
        list_.clear();
    }

    virtual void Remove(Int index)
    {
        if (index >=0 && index < list_.size()) {
            list_.erase(list_.begin() + index);
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, list_.size());
        }
    }

    virtual void RemoveAll(Int from, Int to)
    {
        if (from >=0 && from < list_.size()) {
            if (to >=0 && to < list_.size()) {
                if (to >= from)
                {
                    list_.erase(list_.begin() + from, list_.begin() + to);
                }
                else {
                    throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects [from > to]: ", 0, from, to);
                }
            }
            else {
                throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects [to]: ", to, 0, list_.size());
            }
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects [from]: ", from, 0, list_.size());
        }
    }

    virtual void Insert(Int index, const T &value)
    {
        if (index >=0 && index <= list_.size()) {
            if (index == list_.size()) {
                list_.push_back(value);
            }
            else {
                list_.insert(list_.begin() + index, value);
            }
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, list_.size());
        }
    }

    virtual void Add(const T& value)
    {
        list_.push_back(value);
    }

    static void InitType() {
    	type_ = new AbstractType(TypeNameFactory<Interface>::name(), List::MyType());
    }

    static void InitOperations() {
    	AbstractType* type = static_cast<AbstractType*>(Interface::MyType());

    	type->AddOperation(new BasicOperation<T (Me::*)(Int), const T& (Me::*)(Int) const>(type, "Get", "index", &Me::Get));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, T), void (Me::*)(Int, const T&)>(type, "Set", "index", "value", &Me::Set));
    	type->AddOperation(new BasicOperation<void (Me::*)(T), void (Me::*)(const T&)>(type, "Add", "value", &Me::Add));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, T), void (Me::*)(Int, const T&)>(type, "Insert", "index", "value", &Me::Insert));
    }

    CUBEFS_DECLARE_REFLECTION;
};

template <typename T, typename Interface>
Type* TypedListImplT<T, -1, Interface>::type_ = NULL;




template <typename T, Int Size, typename Interface>
class TypedListImplT: public ListImplT<Interface> {
	typedef TypedListImplT<T, Size, Interface> 	Me;
	typedef ListImplT<Interface>				Base;

    T list_[Size];
    Int size_;

public:
    typedef T Element;

    TypedListImplT(): size_(0) {}
    TypedListImplT(Int capacity): size_(0) {} // ignore capacity

    virtual ~TypedListImplT() throw () {}

    virtual Int GetElementSize() const {
        return sizeof(T);
    }

    virtual Int size() const {
        return size_;
    }

    virtual const T& Get(Int index) const
    {
        if (index >=0 && index < size_) {
            return list_[index];
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, size_);
        }
    }

    virtual T& Get(Int index)
    {
        if (index >=0 && index < size_) {
            return list_[index];
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, size_);
        }
    }

    virtual void Set(Int index, const T& value)
    {
    	if (index >=0 && index < size_) {
    		list_[index] = value;
    	}
    	else {
    		throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, size_);
    	}
    }

    const T& operator[](Int index) const {
        return Get(index);
    }

    virtual void Clear() {
        size_ = 0;
    }

    virtual void Remove(Int index)
    {
        if (index >=0 && index < size_) {
            MoveData(index, index + 1, size_ - index - 1);
            --size_;
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, size_);
        }
    }

    virtual void RemoveAll(Int from, Int to)
    {
        if (from >=0 && from < size_) {
            if (to >=0 && to < size_) {
                if (to >= from)
                {
                    MoveData(to, from, size_ - to);
                    size_ -= (to - from);
                }
                else {
                    throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects [from > to]: ", 0, from, to);
                }
            }
            else {
                throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects [to]: ", to, 0, size_);
            }
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects [from]: ", from, 0, size_);
        }
    }

    virtual void Insert(Int index, const T &value)
    {
        if (index >=0 && index <= size_) {
            if (index == size_) {
                Add(value);
            }
            else if (size_  < Size) {
                MoveData(index + 1, index, size_ - index);
                list_[index] = value;
                ++size_;
            }
            else {
                throw BoundsException(CUBEFS_SOURCE, "List is full", size_, 0, Size);
            }
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "Index out of bounds in ListOfTypedObjects: ", index, 0, size_);
        }
    }

    virtual void Add(const T& value)
    {
        if (size_ < Size) {
            list_[size_++] = value;
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, "List is full", size_, 0, Size);
        }
    }

    static void InitType() {
    	type_ = new AbstractType(TypeNameFactory<Interface>::name(), List::MyType());
    }

    static void InitOperations() {
    	AbstractType* type = static_cast<AbstractType*>(Interface::MyType());

    	type->AddOperation(new BasicOperation<T (Me::*)(Int), const T& (Me::*)(Int) const>(type, "Get", "index", &Me::Get));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, T), void (Me::*)(Int, const T&)>(type, "Set", "index", "value", &Me::Set));
    	type->AddOperation(new BasicOperation<void (Me::*)(T), void (Me::*)(const T&)>(type, "Add", "value", &Me::Add));
    	type->AddOperation(new BasicOperation<void (Me::*)(Int, T), void (Me::*)(Int, const T&)>(type, "Insert", "index", "value", &Me::Insert));
    }

protected:

    void MoveData(Int dest, Int src, Int count) {
        Int c;
        if (src < dest) {
            --src;
            --dest;
            for(c = count; c > 0 ; c--) {
                list_[dest + c] = list_[src + c];
            }
        }
        else {
            for(c = 0; c < count ; c++) {
                list_[dest + c] = list_[src + c];
            }
        }
    }

    CUBEFS_DECLARE_REFLECTION;
};

template <typename T, Int Size, typename Interface>
Type* TypedListImplT<T, Size, Interface>::type_ = NULL;



template <typename T, Int Size = -1, Int ListValueType = ListType<T>::TypeValue> class TypedListImpl;

template <typename T, Int Size>
class TypedListImpl<T, Size, UnknownType>: public TypedListImplT<T, Size, TypedList<T, Size> > {
	typedef TypedListImplT<T, Size, TypedList<T, Size> > Base;
public:
	TypedListImpl(): Base() {}
	TypedListImpl(Int capacity): Base(capacity) {}
};


template <typename T, Int Size>
class TypedListImpl<T, Size, TypedType>: public TypedListImplT<T, Size, TypedList<T, Size> > {
	typedef TypedListImplT<T, Size, TypedList<T, Size> > Base;
public:
	TypedListImpl(): Base() {}
	TypedListImpl(Int capacity): Base(capacity) {}

	virtual Typed* GetElement(Int index) const {
		return Base::Get(index);
	}

	virtual void SetElement(Int index, const Typed* value)
	{
		if (value != NULL)
		{
			if (value->IsInstance(TypeFactory<T>::type()))
			{
				T val = static_cast<T>(const_cast<Typed*>(value));
				Base::Set(index, val);
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			Base::Set(index, NULL);
		}
	}

	virtual void Insert(Int index, const Typed* value)
	{
		if (value != NULL)
		{
			if (value->IsInstance(TypeFactory<T>::type()))
			{
				T val = static_cast<T>(const_cast<Typed*>(value));
				Base::Insert(index, val);
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			Base::Insert(index, NULL);
		}
	}

	virtual void Add(const Typed* value)
	{
		if (value != NULL)
		{
//			cout<<"MyType: "<<TypeFactory<T>::type()<<" "<<TypeFactory<T>::type()->name()<<endl;
//
//			Type* type = value->type();
//
//			while (type != NULL)
//			{
//				cout<<type<<" "<<type->name()<<" "<<type->parent()<<endl;
//				type = type->parent();
//			}

			if (value->IsInstance(TypeFactory<T>::type()))
			{
				T val = static_cast<T>(const_cast<Typed*>(value));
				Base::Add(val);
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			Base::Add(NULL);
		}
	}
};



template <typename T, Int Size>
class TypedListImpl<T, Size, NumberValueType>: public TypedListImplT<T, Size, TypedList<T, Size> > {
	typedef TypedListImplT<T, Size, TypedList<T, Size> > Base;
public:
	TypedListImpl(): Base() {}
	TypedListImpl(Int capacity): Base(capacity) {}

	virtual bool IsElementPrimitive() const	{
		return true;
	}

	virtual Typed* GetElement(Int index) const {
		return GetCoreFactory()->Create(Base::Get(index));
	}

	virtual void GetElement(Int index, Value* value) const {
		if (value != NULL)
		{
			if (value->type() == TypeFactory<T>::type())
			{
				value->set(&Base::Get(index));
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			throw NullPointerException(CUBEFS_SOURCE, "value argument must not be null");
		}
	}

	virtual void SetElement(Int index, const Typed* value) {
		if (value != NULL)
		{
			if (value->type() == TypeFactory<T>::type())
			{
				const Value* v0 = static_cast<const Value*>(value);
				T val;
				v0->get(&val);
				Base::Set(index, val);
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			throw NullPointerException(CUBEFS_SOURCE, "value argument must not be null");
		}
	}

	virtual void Insert(Int index, const Typed* value) {
		if (value != NULL)
		{
			if (value->type() == TypeFactory<T>::type())
			{
				const Value* v0 = static_cast<const Value*>(value);
				T val;
				v0->get(&val);
				Base::Insert(index, val);
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			throw NullPointerException(CUBEFS_SOURCE, "value argument must not be null");
		}
	}

	virtual void Add(const Typed* value) {
		if (value != NULL)
		{
			if (value->type() == TypeFactory<T>::type())
			{
				const Value* v0 = static_cast<const Value*>(value);
				T val;
				v0->get(&val);
				Base::Add(val);
			}
			else {
				throw TypeCastException(CUBEFS_SOURCE, "value argument has invalid type", value->type(), TypeFactory<T>::type());
			}
		}
		else {
			throw NullPointerException(CUBEFS_SOURCE, "value argument must not be null");
		}
	}

	virtual Value* CreateValueHolder() const {
		T value;
		return GetCoreFactory()->Create(value);
	}
};




typedef TypedListImpl<Typed*> 								ListOfTypedImpl;
typedef TypedListImpl<Typed*, StaticListSize> 				StaticListOfTypedImpl;
typedef TypedListImpl<Type*> 								ListOfTypesImpl;
typedef TypedListImpl<StringValue*> 						ListOfStringsImpl;
typedef TypedListImpl<BigInt> 								ListOfBigIntImpl;


}
}

#undef CUBEFS_SOURCE

#endif //_CUBEFS_API_REFLECTION_LIST_HPP
